#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
/*---------------------------------------------------------------------------*/
#define UIP_DRIPADDR0   192
#define UIP_DRIPADDR1   168
#define UIP_DRIPADDR2   110
#define UIP_DRIPADDR3   111
#define UIP_BUFSIZE     420
#define UIP_LISTENPORTS 10
#define UIP_CONNS       20
#define UIP_ETHTYPE_ARP 0x0806
#define UIP_ETHTYPE_IP  0x0800
#define UIP_ETHTYPE_IP6 0x86dd
#define UIP_SETIP (addr, add1,add2,add3,add4)  {uint8_t *temp = (uint8_t*)(addr);\
  temp[0]=(add4),temp[1]=(add2),temp[2]=(add2),temp[3]=(add1);}
#define DEVTAP "/dev/net/tun"
#define TAPLOG "/var/log/taplog"


uint16 htons(uint16 n)
{
    uint16 ret;
    uint8 *p = (uint8 *)&ret;
    
    *p = (uint8)(n>>8);
    *(p+1)=(uint8)((n<<8)>>8);

    return ret;
}
uint16 ntohs(uint16 n)
{
    uint16 ret,temp1,temp2;
    uint8 *p = (uint8 *)&n;
    
    temp1 = *p;
    temp2 = *(p+1);
    ret = (temp1<<8) | temp2;

    return ret;
}
uint32 htonl(uint32 n)
{

}
uint32 ntohl(uint32 n)
{

}


/*---------------------------------------------------------------------------*/
typedef uint8_t   uip_ip4addr_t[4];
typedef uint8_t   uip_ethaddr_t[6];
typedef uint8_t   uip_ip6addr_t[16];
typedef uint16_t  uip_port_t;
typedef struct {
    uip_ip4addr_t ip4addr;
    uip_port_t    lport,rport;
    uint8_t       status;
}  uip_conn_t;
typedef struct uip_eth_hdr {
  uip_ethaddr_t dest;
  uip_ethaddr_t src;
  uint16_t      type;
} uip_ethhdr_t;

/*---------------------------------------------------------------------------*/
static int fd;
static int drop = 0;
static int c,uip_len = 0;
static char uip_buf[UIP_BUFSIZE];
static uip_port_t    uip_lport[UIP_LISTENPORTS];
static uip_conn_t    uip_conn[]UIP_CONNS];
static uip_ip4addr_t uip_hostaddr, uip_netmask;


/*---------------------------------------------------------------------------*/
void httpd_init(void);
void httpd_init(void);
void tftpd_init(void);
void telnetd_init(void);


void uip_init(void)
{
    uip_ip4addr_t ip4addr;
    for(c = 0; c < UIP_LISTENPORTS; ++c) {
        uip_lport[c] = 0;
    }
    for(c = 0; c < UIP_CONNS; ++c) {
      //--> todo
    }
    UIP_SETIP(&uip_hostaddr, 192,168,110,2);
    UIP_SETIP(&uip_netmask,  255,255,266,0);

    httpd_init();
    tftpd_init();
    telnetd_init();
    return;
}
void taplog_init(void)
{
    int fd = open(TAPLOG, O_RDWR|O_CREAT, 0666);
    if(fd <= 0){
        printf("[error] open log file error \n");
        exit(-1);
    }
    dup2(1, fd);
}
void tapdev_init(void) 
{
  char buf[128];
  struct ifreq ifr;
  
  fd = open(DEVTAP, O_RDWR);
  if(fd <= -1) {
    printf("[error]open error \n");
    exit(-2);
  }
  memset(&ifr, 0, sizeof(ifr));
  ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
  if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
    printf("[error]ioctl error \n");
    close(fd);
    exit(-2);
  } else {
    snprintf(buf, sizeof(buf), "ifconfig tap0 inet %d.%d.%d.%d",
      UIP_DRIPADDR0, UIP_DRIPADDR1, UIP_DRIPADDR2, UIP_DRIPADDR3);
    system(buf); 
  }
  printf("[dbg] tapdev_init fd=%d ,if=%s\n", fd, ifr.ifr_name);
  return;
}
/*---------------------------------------------------------------------------*/
void tapdev_dump()
{
    int i=0;
    printf("|||----->  tapdev_dump data   <-------|||")
    for(; i<uip_len; i++){
      if(0 == i%32)
        printf("\n%02x", uip_buf[i]);
      else
        printf(" %02x", uip_buf[i]);
    }
     printf("\n|||----->  tapdev_dump data   <-------|||\n");
}

void tapdev_handle(void)
{
    uint16_t type;
    uip_ethhdr_t *p_ethhdr = (uip_ethhdr_t *)uip_buf;
    
    type = STOH()
    switch(p_ethhdr->type) {
    case 1:
        break;
    case 2:
        break;
    default:
        printf("[error]frame type erro");
        break;
    }
    memset(uip_buf,0,sizeof(uip_buf));
    return;
}

int tapdev_read(void)
{
  int ret;
  fd_set fdset;
  struct timeval tv, now;
  
  tv.tv_sec = 10;
  tv.tv_usec = 0;
  FD_ZERO(&fdset);
  FD_SET(fd, &fdset);
  ret = select(fd + 1, &fdset, NULL, NULL, &tv);
  if(ret == 0) {
    printf("[dbg]read timeout\n");
    return 0;
  }
  ret = read(fd, uip_buf, UIP_BUFSIZE);
  if(ret == -1) {
    printf("[error]tap_dev: tapdev_read: read \n");
  } else {
    uip_len = ret;
    tapdev_dump();
  }
  return ret;
}
/*---------------------------------------------------------------------------*/
void tapdev_send(void)
{
  int ret;
  /*  printf("tapdev_send: sending %d bytes\n", size);*/
  /*  check_checksum(uip_buf, size);*/

  /*  drop++;
  if(drop % 8 == 7) {
    printf("Dropped a packet!\n");
    return;
    }*/
  ret = write(fd, uip_buf, uip_len);
  if(ret == -1) {
    perror("tap_dev: tapdev_send: writev");
    exit(1);
  }
}
int main()
{
  int ret = 0;
  taplog_init();
  tapdev_init();
  uip_init();
 retry:
  if((ret = tapdev_read()) > 0){
    goto retry;
  }
  return 0;
}

